# -*- coding: utf-8 -*-
# @Time    : 2024/6/25 14:25
# @Author  : yujiahao
# @File    : 11_pandas_groupby.py
# @description:Pandas groupby分组操作

'''
在数据分析中，经常会遇到这样的情况：根据某一列（或多列）标签把数据划分为不同的组别，然后再对其进行数据分析。

比如，某网站对注册用户的性别或者年龄等进行分组，从而研究出网站用户的画像（特点）。在 Pandas 中，要完成数据的分组操作，需要使用 groupby() 函数，
它和 SQL 的GROUP BY操作非常相似。

在划分出来的组（group）上应用一些统计函数，从而达到数据分析的目的，比如对分组数据进行聚合、转换，或者过滤。这个过程主要包含以下三步：
    - 拆分（Spliting）：表示对数据进行分组；
    - 应用（Applying）：对分组数据应用聚合函数，进行相应计算；
    - 合并（Combining）：最后汇总计算结果。

'''

import pandas as pd
import numpy as np


def pandas_groupby():
    data = {'Name': ['John', 'Helen', 'Sona', 'Ella'],
            'score': [82, 98, 91, 87],
            'option_course': ['C#', 'Python', 'Java', 'C']}
    df = pd.DataFrame(data)

    print('打印原始数据：\n', df)

    # 创建groupby分组对象
    '''
    使用 groupby() 可以沿着任意轴分组。您可以把分组时指定的键（key）作为每组的组名，方法如下所示：
       - df.groupby("key")
       - df.groupby("key",axis=1)
       - df.groupby(["key1","key2"])
    '''

    # 生成分组groupby对象并查看结果
    print(df.groupby(['Name', 'score']).groups)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 遍历分组数据

    # 查看分组
    grouped = df.groupby('score')
    for key, velues in grouped:
        # 其中key代表分组后字典的键，也就是score
        print(key)
        # 字典对应的值选修的科目
        print(velues)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 应用聚合函数

    data = {'name': ['John', 'Helen', 'Sona', 'Ella'],
            'score': [82, 98, 91, 87],
            'option_course': ['C#', 'Python', 'Java', 'C']}
    df = pd.DataFrame(data)
    grouped = df.groupby('name')
    print(grouped['score'].agg([np.size, np.mean, np.std]))

    '''警告原因：Pandas 提示在未来版本中，传递给 agg 方法的可调用函数的行为将会改变。
       解决方法：使用字符串形式的函数名（如 "mean" 和 "std"）来替代 np.mean 和 np.std，以保持当前行为并消除警告。
    '''

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 组的转换操作

    '''在组的行或列上可以执行转换操作，最终会返回一个与组大小相同的索引对象'''

    # 创建一个示例 DataFrame
    df = pd.DataFrame({
        '种类': ['水果', '水果', '水果', '蔬菜', '蔬菜', '肉类', '肉类'],
        '产地': ['朝鲜', '中国', '缅甸', '中国', '菲律宾', '韩国', '中国'],
        '水果': ['橘子', '苹果', '哈密瓜', '番茄', '椰子', '鱼肉', '牛肉'],
        '数量': [3, 5, 5, 3, 2, 15, 9],
        '价格': [2, 5, 12, 3, 4, 18, 20]
    })

    # 选择 DataFrame 中的数值列
    numeric_cols = df.select_dtypes(include=[np.number]).columns

    # 对数值列按 '种类' 分组后求均值，并返回与原 DataFrame 形状相同的 DataFrame
    # 这里使用 'mean' 字符串而不是 np.mean 函数，以避免 FutureWarning
    mean_transformed = df.groupby('种类')[numeric_cols].transform('mean')
    print(mean_transformed)

    # 定义一个去均值的 lambda 函数
    demean = lambda arr: arr - arr.mean()

    # 对数值列按 '种类' 分组后进行去均值操作，并返回与原 DataFrame 形状相同的 DataFrame
    demean_transformed = df.groupby('种类')[numeric_cols].transform(demean)
    print(demean_transformed)

    # 定义一个自定义函数，用于返回每个分组的前 n 行数据
    def get_rows(df, n):
        return df.iloc[:n, :]

    # 使用 apply 方法将自定义函数应用于每个分组，并返回结果
    # 这里我们要求每个分组返回前 1 行数据
    custom_function_applied = df.groupby('种类').apply(get_rows, n=1)
    print(custom_function_applied)


def main():
    pandas_groupby()


if __name__ == '__main__':
    main()
